/*
 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { Command } from "commander"
import { OH_SDK_COMPONENT } from "../oh-sdk-utils.mjs"

const CLI_OPTIONS = new Map()
let EXPECTED_CLI_OPTIONS = []

export const OH_CLI_OPT_KEYS = {
    clean: "clean",
    pubEtsBuildTools: "pubEtsBuildTools",
    target: "target",
    sdkComponent: "sdkComponent",
    sdkVersion: "sdkVersion",
    arch: "arch",
    runtime: "runtime",
    mod: "mod",
    install: "install",
    uninstall: "uninstall",
    run: "run",
    runTimeout: "runTimeout",
    ohConf: "ohConf",
    hdcExtraOpts: "hdcExtraOpts",
    recvImages: "recvImages",
    listDevices: "listDevices",
    testsRoot: "testsRoot",
    pathKey: "pathKey",
    nameKey: "nameKey",
    verbose: "verbose",
}

const STRING_VALUES = []

function defineAllOptions() {
    CLI_OPTIONS.clear()

    defineOption(OH_CLI_OPT_KEYS.clean, {
        flag: "--clean",
        description: "clean artefacts"
    })
    defineOption(OH_CLI_OPT_KEYS.pubEtsBuildTools, {
        flag: "--pub-ets-build-tools",
        description: "publish OpenHarmony SDK ETS with 'build-tools'"
    })
    defineOption(OH_CLI_OPT_KEYS.target, {
        flag: "--target",
        description: "target platform",
        values: ["windows", "linux", "macos"]
    })
    defineOption("sdkComponent", {
        flag: "--sdk-component",
        description: "specify OpenHarmony SDK component",
        values: [OH_SDK_COMPONENT.ets, OH_SDK_COMPONENT.js, OH_SDK_COMPONENT.native, OH_SDK_COMPONENT.toolchains]
    })
    defineOption("sdkVersion", {
        flag: "--sdk-version",
        description: "print OpenHarmony SDK version"
    })
    defineOption(OH_CLI_OPT_KEYS.arch, {
        flag: "--arch",
        description: "CPU architecture",
        values: ["arm64", "arm", "universal"],
        default: "arm64"
    })
    defineOption(OH_CLI_OPT_KEYS.runtime, {
        flag: "--runtime",
        description: "VM runtime",
        values: ["ark"],
        default: "ark"
    })
    defineOption(OH_CLI_OPT_KEYS.mod, {
        flag: "--mod",
        description: "app mode",
        values: ["app", "test", "test-gen-golden"],
        default: "app"
    })
    defineOption(OH_CLI_OPT_KEYS.install, {
        flag: "--install",
        description: "install hap on device"
    })
    defineOption(OH_CLI_OPT_KEYS.uninstall, {
        flag: "--uninstall",
        description: "uninstall hap from device"
    })
    defineOption(OH_CLI_OPT_KEYS.run, {
        flag: "--run",
        description: "run installed hap on device"
    })
    defineOption(OH_CLI_OPT_KEYS.runTimeout, {
        flag: "--run-timeout <timeout-ms>",
        description: "run installed hap on device with timeout; the app is forcedly stopped after timeout"
    })
    defineOption(OH_CLI_OPT_KEYS.ohConf, {
        flag: "--oh-conf <path>",
        description: "path to .ohconf.json",
        default: "./.ohconf.json"
    })
    defineOption(OH_CLI_OPT_KEYS.deviceAddress, {
        flag: "--device-address <address>",
        description: "provide socket address in form of ip:port"
    })
    defineOption(OH_CLI_OPT_KEYS.hdcExtraOpts, {
        flag: "--hdc-extra-opts <opts>",
        description: "provide extra HDC options"
    })
    defineOption(OH_CLI_OPT_KEYS.recvImages, {
        flag: "--recv-images <dst-path>",
        description: "receive images from the app's home"
    })
    defineOption(OH_CLI_OPT_KEYS.listDevices, {
        flag: "--list-devices",
        description: "list connected devices"
    })
    defineOption(OH_CLI_OPT_KEYS.testsRoot, {
        flag: "--tests-root <path>",
        description: "path to tests root folder, containing .ohconf.json",
        default: process.cwd()
    })
    defineOption(OH_CLI_OPT_KEYS.pathKey, {
        flag: "--path-key",
        description: "print path value from .ohconf.json",
        values: STRING_VALUES    })
    defineOption(OH_CLI_OPT_KEYS.nameKey, {
        flag: "--name-key",
        description: "print name value from .ohconf.json",
        values: STRING_VALUES
    })
    defineOption(OH_CLI_OPT_KEYS.verbose, {
        flag: "--verbose",
        description: "operate verbosely"
    })
}

/**
 * Provide an array of expected CLI options keys.
 * By default, all options are expected.
 *
 * @see OH_CLI_OPT_KEYS
 */
export function parseOhCliOptions(expectedOptions) {
    if (expectedOptions)
        EXPECTED_CLI_OPTIONS = expectedOptions

    defineAllOptions()

    const cmd = new Command()

    CLI_OPTIONS.forEach(option => setOption(cmd, option))

    cmd.parse(process.argv)
    const opts = cmd.opts()

    CLI_OPTIONS.forEach(option => checkOption(option, opts))

    return opts
}

function defineOption(key, object) {
    if (EXPECTED_CLI_OPTIONS.length > 0 && !EXPECTED_CLI_OPTIONS.includes(key))
        return
    object.key = key
    CLI_OPTIONS.set(key, object)
}

function setOption(cmd, option) {
    const flag = option.values ? `${option.flag} <value>` : option.flag
    const desc = option.values && option.values !== STRING_VALUES ?
        `${option.description}: ${option.values}` : option.description
    return option.default ?
        cmd.option(flag, desc, option.default) :
        cmd.option(flag, desc)
}

function checkOption(option, opts) {
    const value = opts[option.key]
    if (value && option.values && option.values !== STRING_VALUES && !option.values.includes(value)) {
        console.error(`> '${option.flag}' option value is unknown: '${value}' (supported: ${option.values})`)
        process.exit(1)
    }
}
